import asyncio
from aiohttp import web
from .routes import *


@web.middleware
async def error_middleware(request, handler):
    try:
        response = await handler(request)
        if response.status != 404:
            return response
        message = response.message
    except web.HTTPException as ex:
        if ex.status != 404:
            raise
        message = ex.reason
    return web.json_response({'error': message})


def configure_cors(app):
    import aiohttp_cors
    # 配置跨域，如果前后端分端口部署的话
    cors = aiohttp_cors.setup(app, defaults={
        "*": aiohttp_cors.ResourceOptions(
            allow_credentials=True,
            expose_headers=("X-Custom-Server-Header",),
            allow_headers=("X-Requested-With", "Content-Type"),
        )
    })
    # 将跨域配置到每个路由
    for route in list(app.router.routes()):
        cors.add(route)


class Server(object):
    '''
    后台服务类，集成了 web 服务端和 websocket 服务端，接口如下：
    - run() 启动服务器
    - setup_controller(controller) 设置全局感知模块
    - setup_virtual_uppercomputer(upper) 启动虚拟上位机，运转虚拟码头
    '''

    def __init__(self, config: dict = None):
        '''
        参数如下
        - config 服务端基本配置，结构如下
        {
            'host': 主机地址，默认 '127.0.0.1'
            'port': 端口，默认 8080
            'front' 前端静态页面所在根目录，默认 './front'
        }
        '''
        self.host = config.get('host', '127.0.0.1')
        self.port = config.get('port', 8080)
        self.front = config.get('front', './front')
        self.admin = config.get('admin', './admin')

        self.app = self._create_app()

    def _create_app(self):
        # 创建 app 实例
        app = web.Application(middlewares=[error_middleware])
        # 注册路由
        setup_pages(app, self.front, self.admin)
        # 设置跨域
        configure_cors(app)
        # websocket 服务
        app['websockets'] = []
        # 虚拟上位机
        app['upper'] = None

        # 关闭服务程序的时候，关闭已注册的东西
        app.on_shutdown.append(self._close_websockets)

        return app

    def setup_controller(self, controller):
        '''
        添加全局感知模块实例
        '''
        # 保存实例
        # self.controller = controller
        # 建立 websocket 服务
        setup_websocket(self.app, controller)
        # 暴露 api
        setup_api(self.app, controller)
    
    def setup_virtual_uppercomputer(self, upper):
        '''
        运行虚拟上位机
        '''
        self.upper = upper
        # 服务开启的时候，启动虚拟上位机
        self.app.on_startup.append(self._start_background_upper)
        # 清理开始时，停止正在运行的虚拟上位机
        self.app.on_cleanup.append(self._cleanup_background_upper)

    def run(self):
        '''
        启动服务器
        '''
        web.run_app(self.app, host=self.host, port=self.port)

    async def _start_background_upper(self, app):
        '''
        启动后台控制器
        '''
        app['upper'] = asyncio.create_task(self.upper.update())


    async def _close_websockets(self, app):
        # 释放所有正在建立连接的 websocket
        for ws in app['websockets']:
            await ws.close(code=1001, message='Server shutdown')
        print('close %d websockets' % len(app['websockets']))

    async def _cleanup_background_upper(self, app):
        # 关闭后台控制器
        app['upper'].cancel()
        await app['upper']
        print('close background virtual upper computer')
